import asyncio
from dataclasses import dataclass
from typing import Callable

import pywebio
from pywebio.io_ctrl import Output

from emi.core.task import Task, TaskManager, TaskStatus
from pywebio import  output

from emi.util import AsyUtil

from emiui import dialog


class UISession:

    @staticmethod
    def get(taskManger:TaskManager = None):
        """
        初始化一个ui session
        :param taskManger:  该session会监听taskManagr对象的任务执行。
        :return:
        """
        session = UISession(taskManger)
        session._pywebio_session = pywebio.session.get_current_session()
        session._looper = asyncio.get_running_loop()
        assert session._pywebio_session is not None
        assert session._looper is not None
        session._pywebio_session.defer_call(session._on_destroy)
        ##setattr(session._pywebio_session,"_ui_session_",session)
        return session

    def __init__(self,taskManager:TaskManager):
        self._pywebio_session = None
        self._looper = None
        self._task_manager = taskManager
        self.task_changed_lisener = None  ### 任务监听器。
        self._task_map = {}
        self._on_create()


    def _on_create(self):
        """
        session创建时调用。
        :return:
        """
        if self._task_manager:
            self._task_manager.add_task_changed_listener(self)

    def _on_destroy(self):
        """
        session销毁时调用
        :return:
        """
        print(f"_on_destroy")
        self.task_changed_lisener = None
        if self._task_manager:
            self._task_manager.remove_task_changed_listener(self)

    def on_task_changed(self,task:Task):
        self.post(self._on_update_task_in_pywebio_session(task))

    def post(self, coro_obj):
        """
        post指定的pywebio 的session环境里面执行。
        :return:
        """
        def _call_():
            self._pywebio_session.run_async(coro_obj)
        self._looper.call_soon(_call_)


    async def _on_update_task_in_pywebio_session(self,task:Task):
        await self._update_task_ui(task)
        ls = self.task_changed_lisener
        if ls:
            ls(task)

    def post_show(self,scope_name: str,thread: Callable,show:Callable,loading = True) -> Output:
        """
        异步方式put组建
        :param scope_name:
        :param thread: 在thread 线程处理耗时操作，并返回一个对象
        :param show: 在show 线程处理显示对象
        :return:
        """
        ret = output.put_scope(scope_name)
        if loading:
            async def show_loading():
                with output.use_scope(scope_name,clear=True):
                    output.put_loading()
            self.post(show_loading())


        async def _show_data(data):
            with output.use_scope(scope_name, clear=True):
                show(data)

        def _run_in_back_ground():
            ret = thread()
            if isinstance(ret,set):
                ### 来自lambda 表达式
                ret = ret.pop()
            self.post(_show_data(ret))

        AsyUtil.run_in_thread(_run_in_back_ground)
        return ret

    def put_task(self,task: Task, label: str = False, small_type:bool = None,confirm_tips:str = None, stopable: bool = False, onfinished=None,clickable:bool=True):
        _scope_name = f"_session_task_{task.id}_scope"

        # has_set_scope = self._task_map.get(_scope_name)
        # if has_set_scope:
        #     raise RuntimeError(f"task:{task.id}-{task.get_name()}已经put过！")
        ret = output.put_scope(_scope_name)
        task_session = TaskSession(task=task)
        self._task_map[_scope_name] = task_session
        task_session.label = label
        task_session.stopable = stopable
        task_session.confirm_tips = confirm_tips
        task_session.on_finish_call_back = onfinished
        task_session.small_button_stype = small_type
        task_session.clickable = clickable
        pywebio.session.run_async(self._update_task_ui(task))
        return ret

    async def _update_task_ui(self,task:Task):
        _scope_name = f"_session_task_{task.id}_scope"
        task_session:TaskSession = self._task_map.get(_scope_name)
        if not task_session:
            return
        is_chagned = task_session.old_status is None
        if not is_chagned:
            is_chagned = task_session.old_status != task.status
        if not is_chagned:
            return
        with output.use_scope(_scope_name,clear=True):
            old_status_is_running = task_session.old_status.is_run() if task_session.old_status else False
            task_session.old_status = task.status
            if task.status.is_run():
                output.put_row([
                    output.put_loading()
                ])
            elif task_session.clickable:
                label = task_session.label if task_session.label else "运行"
                async def _do_run_action():
                    if task_session.confirm_tips is not None:
                        is_confirm = await  dialog.popup_confirm(task_session.confirm_tips)
                        if not is_confirm:
                            return
                    self._task_manager.run_task(task)

                output.put_row([
                    output.put_button(label=label,small=task_session.small_button_stype,onclick=_do_run_action)
                ])
            else:
                #### 只能看
                output.put_row([
                    output.put_text("空闲")
                ])
            if old_status_is_running and task.status == TaskStatus.FINISH:
                if task_session.on_finish_call_back:
                    call_back = task_session.on_finish_call_back
                    self.post(call_back())


@dataclass
class TaskSession:

    task:Task = None
    old_status:TaskStatus = None
    label:str = None
    small_button_stype:bool = None
    on_finish_call_back = None
    confirm_tips:str = None  ## 不为None时，弹出确认窗口
    clickable:bool = True   ## 是否可以点击，如果为False，则只能看运行状态。


